WebRTC源码分析 | 您所在的位置:网站首页 › webrtc track › WebRTC源码分析 |
目录
1. 引言2. 视频轨道的创建和添加2.1 视频源的创建2.1.1 创建视频采集2.1.1.1 视频采集UML2.1.1.2 采集模块的内部数据流
2.1.2 创建视频源2.1.3 创建视频轨源
2.2 视频轨的创建2.2.1 PeerConnection::CreateVideoTrack2.2.2 视频轨的继承树
2.3 视频数据的流动2.4 添加视频轨到PeerConnection
3 总结
1. 引言
创建完PeerConnectionFactory 和 PeerConnection这两个API层的操盘对象之后,紧接着需要初始化本地的媒体,也即创建本地的音频轨、视频轨、数据通道,并将这些本地的媒体轨道添加到PeerConnection对象中。如图中红色标注所示。 本文将详细描述上述视频轨道的创建细节 以及 轨道被添加到PeerConnection中的存储情况。 从上创建视频源的代码可以知道如下几点: 通过设备信息类VideoCaptureModule::DeviceInfo获取音视频设备个数需要根据预设的视频参数宽高,帧率去创建视频源对象VcmCapturer,一旦音频源对象VcmCapturer创建成功,则认为找到了底层适用于该预设视频参数的音频采集VideoCapture设备。以创建的视频源对象VcmCapturer为入参,构建视频轨源对象CapturerTrackSource。VcmCapturer成为CapturerTrackSource的私有成员。CapturerTrackSource、VcmCapturer、VideoCapture对象的继承树,以及这三者的关系如下UML类图所示: 视频采集模块是数据流水线的起始点,负责从视频源采集原始视频帧,推送给流水线的下一站:可以是本地渲染模块进行本地回显,也可以是编码模块进行数据编码压缩。 视频源可以是摄像头,也可以是桌面、窗口抓屏(远程桌面,基于视频流的电子白板等应用),甚至可以是磁盘上的视频文件,图片文件。WebRTC中提供了基于摄像头的视频采集框架,是本文要讨论的重点。当然WebRTC也提供了桌面,窗口抓屏框架,这套框架对外所提供的接口与基于摄像头的采集接口有所不同。整个视频流水线建立是以摄像头采集接口为基础的,从而导致这么个问题:当需要将抓屏数据当做视频源往外推送时,需要使用适配器模式来实现一套基于摄像头的视频采集接口。 视频采集模块是平台相关的模块,MacOS/IOS一般使用AVFoundation框架或者QuikTime框架,Linux平台一般使用V4L2库,Android上一般使用Camera1或者Camera2框架,Windows平台则使用DS(DirectShow)或者是MF(MediaFoundation)。由于WebRTC是个非常活跃的工程,代码架构一直在不停的变动之中,比如2019年4月份的代码还有VideoCaptureMF的代码,并且还注释着Vista及以上的版本建议使用MediaFoundation采集框架,而2019年11月份的代码MediaFoundation相关的代码已经被移除。再比如MacOs/IOS,Android的相关代码已经被移动到sdk/objc和sdk/android目录下。本文以modules/video_capture下的代码来做阐述,平台无关的代码在该直接目录下,平台相关的实现在modules/video_capture/windows,modules/video_capture/linux目录下,如图所示:
VideoCaptureModule视频采集模块的虚基类,它定义一系列视频采集的通用接口函数: Start/StopCapture用来开始/结束视频采集(平台相关);CaptureStarted用来判断当前capture运行状态(平台相关);Register/DeCaptureDataCallback用来注册/注销数据回调模块(平台无关);Set/GetApplyRotation用来设置视频旋转角度(平台无关)。VideoCaptureImpl类是VideoCaptureModule的实现子类。做了3个事: 声明静态Ctreate方法,用于创建平台相关的VideoCaptureImpl子类,在Windows平台上为VideoCaptureDS,在Linux平台上实现的子类是VideoCaptureV4L2。该方法一处声明,多处实现,在相应平台编译时,只会加载对应平台的实现代码;平台相关的接口,留待平台相关的子类中实现,主要是开始/结束视频采集;实现平台无关的接口:注册视频数据回调,应用视频旋转相关函数。其中注册数据回调将一个实现了VideoSinkInterface接口的对象赋予VideoCaptureImpl::_dataCallBack成员。当采集模块得到一帧视频数据,就可以通过该对象的OnFrame()方法推送出来。 2.1.1.2 采集模块的内部数据流VcmCapturer是创建的视频源对象,虽然从名字上来看像是视频采集类,但实质上它实现了VideoSourceInterface接口。我们认为其是一个视频源。 VcmCapturer在构造成功时就会启动VideoCaptureModule进行视频采集。 视频源VcmCapturer持有一个非常重要的成员VideoBroadcaster对象,该对象的UML类图如下。 为什么要如此设计?因为,在WebRTC 1.0的官方规范中说明了一个视频源是可以被多个视频轨共用的。通过上述方式可以实现共用的概念。 2.1.3 创建视频轨源
VideoTrackSource另外实现了视频源状态相关的接口,以及状态通告相关的接口NotifierInterface,用于向更高一层(VideoTrack)通告视频源的状态。 2.2 视频轨的创建 2.2.1 PeerConnection::CreateVideoTrack rtc::scoped_refptr video_track_( peer_connection_factory_->CreateVideoTrack(kVideoLabel, video_device)); rtc::scoped_refptr PeerConnectionFactory::CreateVideoTrack( const std::string& id, VideoTrackSourceInterface* source) { RTC_DCHECK(signaling_thread_->IsCurrent()); rtc::scoped_refptr track( VideoTrack::Create(id, source, worker_thread_)); return VideoTrackProxy::Create(signaling_thread_, worker_thread_, track); } rtc::scoped_refptr VideoTrack::Create( const std::string& id, VideoTrackSourceInterface* source, rtc::Thread* worker_thread) { rtc::RefCountedObject* track = new rtc::RefCountedObject(id, source, worker_thread); return track; } VideoTrack::VideoTrack(const std::string& label, VideoTrackSourceInterface* video_source, rtc::Thread* worker_thread) : MediaStreamTrack(label), worker_thread_(worker_thread), video_source_(video_source), content_hint_(ContentHint::kNone) { video_source_->RegisterObserver(this); }由上视频轨创建过程可知, 我们最终创建的实体视频轨是VideoTrack这个类的对象,但是向应用层返回的是相对应的代理对象VideoTrackProxy,这是WebRTC防止线程乱入的常规操作,原因不再多赘述。另外由代理对象的构建方式可知,VideoTrack的部分方法需要在信令线程执行,部分方法需要在工作者线程执行。VideoTrack成为源的观察者,可以获知源的状态改变。 2.2.2 视频轨的继承树如下是VideoTrack类的UML类图。 纵观上述几个对象,我们可以得出如下的类图: 虽然比较细致地拆解了视频源为VideoTrackSource、VideoSource、VideoCapture,但从宏观概念上,我们要知晓:可以直接认为VideoTrackSource是视频源。数据从视频源流向视频轨。 视频轨既要从视频源获取视频数据,还要观察视频源地状态,从而同步更改自己地状态。 2.4 添加视频轨到PeerConnection由于VideoTrack同AudioTrack一样,都实现了MediaStreamTrackInterface接口,对于PC而言,视频轨和音频轨并无实质区别,因为都是通过如下方法进行添加的。因此,在此处就不再赘述VideoTrack如何被添加?存储在PC的何处?是否将引发PC状态的改变? 等等,见上篇文章:WebRTC源码分析-呼叫建立过程之四(上)(创建并添加本地音频轨到PeerConnection) RTCErrorOr AddTrack( rtc::scoped_refptr track, const std::vector& stream_ids) override; 3 总结行文知此,大致对如何创建一个视频轨并添加到PC进行了一个较细致的分析。即便忘记了细节,那么如下要点是需要记住的 视频轨的实体对象是VideoTrack类,对于应用层返回的是其代理对象VideoTrackProxy,这是WebRTC为了防止线程乱入所作的常规操作。WebRTC中,媒体数据总是从Source流向Sink。某个对象要想从VideoTrack获取到视频帧,那么它需要实现VideoSinkInterface,并注册到VideoTrack中。实质上向VideoTrack注册的Sink会被传递到VideoTrack所持有的VideoSource的VideoBroadcast成员对象中,从那儿可以获得从VideoCapture传递来的视频帧。记住VideoTrack提供了ContentHint这样一个属性以及设置这个属性的接口。该属性描绘了视频帧的内容,这个会影响到编码器的降级策略(当网络带宽资源 && CPU资源受限时,无法按照既定的编码参数进行编码输出,那么就需要降级编码)是保持帧率,还是保持分辨率。当我们做远程桌面、电子白板这种应用时,我们认为内容清晰度比流畅性更重要,此时就需要保持分辨率,降低一些帧率。那么将VideoTrack的ContentHint属性设置为kDetailed or kText,那么就暗示了编码器保持分辨率;当我们做音视频会话时,我们需要保证音视频的流畅性、连贯性,因此,一般会采取保帧率,降分辨率的策略,那么可以将VideoTrack的ContentHint属性设置为kFluid,那么暗示编码器保持帧率。 |
CopyRight 2018-2019 实验室设备网 版权所有 |